/*
 * @Author: 火红色祥云 809880192@qq.com
 * @Date: 2023-03-09 19:15:57
 * @LastEditors: 火红色祥云 809880192@qq.com
 * @LastEditTime: 2023-03-12 11:02:05
 * @FilePath: /imx6ull-app/21_mpu6050/21_mpu6050.c
 * @Description: mpu6050加速度传感器设备驱动程序  iic地址0x68
 */
#include "mpu6050.h"
#include "linux/init.h"
#include "linux/module.h"
#include "linux/kernel.h"
#include "linux/ide.h"

#include "linux/i2c-dev.h"
#include "linux/i2c.h"

#include "linux/fs.h"

#include "linux/cdev.h"

#include "linux/delay.h"

#define IIC_WEITE   0
#define IIC_READ    1

#define MPU6050_ADDR    0x68

dev_t mpu6050_devid;
int mpu6050_major;
int mpu6050_minor;
struct cdev mpu6050_cdev;
struct class *mpu6050_class;
void *mpu6050_client;

/************************************iic读写***************************************/
static int iic_write_regs(struct i2c_client *client,u8 reg,u8 *buf,u8 len)
{
    u8 send_buf[256];
    struct i2c_client *m_client = client;
    struct i2c_msg msg;

    send_buf[0] = reg;                  //将要写的寄存器放入发送数组
    memcpy(&send_buf[1],buf,len);       //将要发送的数据放入发送数组中

    msg.addr = m_client->addr;            //client->addr来自设备树中设置的地址
    msg.flags = IIC_WEITE;              //iic的读写方向
    msg.buf = send_buf;                 //发送的数据
    msg.len = len + 1;                  //发送的数据长度+寄存器的地址长度       

    return i2c_transfer(m_client->adapter,&msg,1);               
}

static int iic_read_regs(struct i2c_client *client,u8 reg,void *value,u8 len)
{
    int retval = 0;
    struct i2c_msg msg[2];      //一个读  一个写
    struct i2c_client *m_client = client;

    //写
    msg[0].addr = m_client->addr;
    msg[0].flags = IIC_WEITE;
    msg[0].buf = &reg;
    msg[0].len = 1;

    //读
    msg[1].addr = m_client->addr;
    msg[1].flags = IIC_READ;
    msg[1].buf = value;
    msg[1].len = len;

    retval = i2c_transfer(m_client->adapter,msg,2);

    if (retval == 2)
    {
        return 0;
    }
    else
    {
        printk("iic read falied:%d reg = %06x len=%d\r\n",retval,reg,len);
        retval = -EREMOTEIO;
    }

    return retval;
}

/************************************mpu6050读写***********************************/
static unsigned char mpu6050_read_reg(struct i2c_client *client,u8 reg)
{
    u8 data;
    iic_read_regs(client,reg,&data,1);
    return data;
}

static u8 mpu6050_write_reg(struct i2c_client *client,u8 reg,u8 data)
{
    return iic_write_regs(client,reg,&data,1);
}

/************************************mpu6050功能配置*******************************/
static u8 mpu6050_set_gyro_fsr(u8 fsr)
{
    return mpu6050_write_reg((struct i2c_client *)mpu6050_client,MPU_GYRO_CFG_REG,fsr<<3);
}

static u8 mpu6050_set_accel_fsr(u8 fsr)
{
    return mpu6050_write_reg((struct i2c_client *)mpu6050_client,MPU_ACCEL_CFG_REG,fsr<<3);
}
//设置低通滤波
static u8 mpu6050_set_lpf(u16 lpf)
{
    u8 data = 0;
    if(lpf >= 188)data=1;
    else if(lpf >= 98)data=2;
    else if(lpf >= 42)data=3;
    else if(lpf >= 20)data=4;
    else if(lpf >= 10)data=5;
    else data=6;
    return mpu6050_write_reg((struct i2c_client *)mpu6050_client,MPU_CFG_REG,data);
}
//设置采样频率
static u8 mpu6050_set_rate(u16 rate)
{
    u8 data;
    if(rate > 1000)rate = 1000;
    if(rate < 4) rate = 4;
    data = 1000/rate - 1;
    data = mpu6050_write_reg((struct i2c_client *)mpu6050_client,MPU_SAMPLE_RATE_REG,data);
    return mpu6050_set_lpf(rate/2);
}
//获取温度数据
static u16 mpu6050_get_temperature(void)
{
    u8 buf[2];
    int retval=0;
    short temp=0;
    float temperature=0;
    memset(buf,0.,2);
    retval = iic_read_regs((struct i2c_client *)mpu6050_client,MPU_TEMP_OUTH_REG,buf,2);
    if(retval == 0)
    {
        temp = (((u16)(buf[0]<<8)) | buf[1]);
        //temperature = 36.53+((double)temp)/340;
    }
    return (u16)(temperature*100);
}

//获取陀螺仪数据
static u8 mpu6050_get_gyro(short *gx,short *gy,short *gz)
{
    u8 buf[6];
    int retval;
    retval = iic_read_regs((struct i2c_client *)mpu6050_client,MPU_GYRO_XOUTH_REG,buf,6);
    if (retval == 0)
    {
        *gx = ((u16)(buf[0]<<8)) | buf[1];
        *gy = ((u16)(buf[2]<<8)) | buf[3];
        *gz = ((u16)(buf[4]<<8)) | buf[5];
    }
    return retval;
}

//获取加速度计数据
static u8 mpu6050_get_acce(short *ax,short *ay,short *az)
{
    u8 buf[6];
    int retval;
    retval = iic_read_regs((struct i2c_client *)mpu6050_client,MPU_ACCEL_XOUTH_REG,buf,6);
    if(retval == 0)
    {
        *ax = ((u16)(buf[0]<<8)) | buf[1];
        *ay = ((u16)(buf[2]<<8)) | buf[3];
        *az = ((u16)(buf[4]<<8)) | buf[5];
    }
    return retval;
}

/************************************mpu6050fops**********************************/
int mpu6050_open (struct inode *nd, struct file *fp)
{
    fp->private_data = mpu6050_client;
    return 0;
}
ssize_t mpu6050_read(struct file *fp, char __user *buf, size_t len, loff_t *offset)
{
    short gyrox=0,gyroy=0,gyroz=0;
    short accx=0,accy=0,accz=0;
    short temperature=0;
    int retval=0;
    short databuf[7];

    memset(databuf,0,sizeof(databuf));

    //temperature = mpu6050_get_temperature();
    mpu6050_get_gyro(&gyrox,&gyroy,&gyroz);
    mpu6050_get_acce(&accx,&accy,&accz);

    databuf[0] = 0;
    databuf[1] = gyrox;
    databuf[2] = gyroy;
    databuf[3] = gyroz;
    databuf[4] = accx;
    databuf[5] = accy;
    databuf[6] = accz;

    retval = copy_to_user(buf,databuf,sizeof(databuf));

    return retval;
}
int mpu6050_release (struct inode *nd, struct file *fp)
{

    return 0;
}
static struct file_operations mpu6050_fops = {
    .owner = THIS_MODULE,
    .open = mpu6050_open,
    .read = mpu6050_read,
    .release = mpu6050_release,
};
/*********************************************************************************/

int mpu6050_probe(struct i2c_client *client, const struct i2c_device_id *dev_id)
{
    int id;
    printk("mpu6050_probe exec\r\n");

    //申请设备号
    alloc_chrdev_region(&mpu6050_devid,0,1,"mpu6050_device");
    mpu6050_major = MAJOR(mpu6050_devid);
    mpu6050_minor = MINOR(mpu6050_devid);
    printk("mpu6050_major:%d\tmpu6050_minor:%d\r\n",mpu6050_major,mpu6050_minor);

    //注册字符设备驱动
    printk("register char device driver\r\n");
    mpu6050_cdev.owner = THIS_MODULE;
    cdev_init(&mpu6050_cdev,&mpu6050_fops);
    cdev_add(&mpu6050_cdev,mpu6050_devid,1);

    //创建类/设备节点
    printk("create device node\r\n");
    mpu6050_class = class_create(THIS_MODULE,"mpu6050_device");
    device_create(mpu6050_class,NULL,mpu6050_devid,NULL,"mpu6050_device");

    //传递iic适配器
    mpu6050_client = client;
    printk("device create done\r\n");

    //初始化mpu6050

    //复位6050
    mpu6050_write_reg(client,MPU_PWR_MGMT1_REG,0x80);
    mdelay(100);
    mpu6050_write_reg(client,MPU_PWR_MGMT1_REG,0x00);

    //配置6050
    mpu6050_set_gyro_fsr(3);
    mpu6050_set_accel_fsr(0);
    mpu6050_set_rate(50);
    mpu6050_write_reg(client,MPU_INT_EN_REG,0x00);
    mpu6050_write_reg(client,MPU_USER_CTRL_REG,0x00);
    mpu6050_write_reg(client,MPU_FIFO_EN_REG,0x00);
    mpu6050_write_reg(client,MPU_INTBP_CFG_REG,0x80);
    id = mpu6050_read_reg(client,MPU_DEVICE_ID_REG);
    printk("mpu6050_id:%d\r\n",id);
    if(id == MPU6050_ADDR)
    {
        mpu6050_write_reg(client,MPU_PWR_MGMT1_REG,0x01);
        mpu6050_write_reg(client,MPU_PWR_MGMT2_REG,0x00);
        mpu6050_set_rate(50);
    }
    return 0;
}

int mpu6050_remove(struct i2c_client *client)
{
    unregister_chrdev_region(mpu6050_devid,1);
    cdev_del(&mpu6050_cdev);
    device_destroy(mpu6050_class,mpu6050_devid);
    class_destroy(mpu6050_class);
    return 0;
}

struct of_device_id mpu6050_of_match_table[] = {
    {
        .compatible = "lux,mpu6050",
    },
    {

    },
};
struct of_device_id mpu6050_id_table[] = {
    {"lux,mpu6050",0},
    {}
};

static struct i2c_driver mpu6050_driver = {

    .probe = mpu6050_probe,
    .remove = mpu6050_remove,
    .driver = {
        .owner = THIS_MODULE,
        .of_match_table =  mpu6050_of_match_table,
        .name = "MPU6050",
    },
    .id_table = mpu6050_id_table,
};
/**********************************************************************************/

static int __init mpu6050_init(void)
{
    return i2c_add_driver(&mpu6050_driver);
}

static void __exit mpu6050_exit(void)
{
    i2c_del_driver(&mpu6050_driver);
}

module_init(mpu6050_init);
module_exit(mpu6050_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Lux");
MODULE_DESCRIPTION("MPU6050 for test IIC bus");


